﻿/*
 * Copyright (c) 2011, The Chancellor, Masters and Scholars of the University of Oxford
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list of 
 *   conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this list 
 *   of conditions and the following disclaimer in the documentation and/or other 
 *   materials provided with the distribution.
 *
 * Neither the name of University of Oxford nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior 
 * written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Vreci.Metadata.Dicom
{
    /// <summary>
    /// This class enables DICOM Metadata to be filtered.
    /// </summary>
    public class DicomFilter
    {
        VreciProperties filter;

        public DicomFilter()
        {
            filter = new VreciProperties();
        }

        public string ReadFilter(XDocument doc)
        {           
            return filter.Read(doc);
        }

        public string EvaluateResult(object result)
        {
            string value = "";
            if (result == null)
            {
                value = "";
            }
            else if (result is string)
            {
                value = (string)result;
            }
            else if (result is bool)
            {
                value = "" + result;
            }
            else if (result is double)
            {
                value = "" + result;
            }
            else if (result is System.Collections.IEnumerable)
            {

                foreach (var r in ((IEnumerable<object>)result))
                {
                    value += EvaluateResult(r);
                }
            }
            else
            {
                value = result.ToString();
            }

            return value;
        }

        public XDocument Filter(XDocument dicom_doc, VreciProperties existingProperties)
        {
            return FilterToProperties(dicom_doc,existingProperties).Write();
        }

        public VreciProperties FilterToProperties(XDocument dicom_doc, VreciProperties existingProperties)
        {
            VreciProperties properties = new VreciProperties();
            
            //Go through each property in the filter and calculate a property for output.
            foreach (var filterProperty in filter.Properties)
            {
                //Work out the value (If is is an XPath then use that)

                //If it already exists in existing properties and has manually edited set then use that.
                VreciPropertyItem existing_item = null;
                foreach (var item in existingProperties.Properties)
                {
                    if (item.Reference == filterProperty.Reference)
                    {
                        existing_item = item;
                        break;
                    }
                }

                if (existing_item != null && existing_item.ManuallyEdited)
                {
                    properties.Properties.Add(existing_item);
                } else {
                    string value = null;

                    string XPathTerm = null;
                    if (filterProperty.UseValueAsXPath)
                    {
                        XPathTerm = filterProperty.Value;
                    }
                    else if (filterProperty.Tag != null)
                    {
                        XPathTerm = "/dicom/attr[@tag='" + filterProperty.Tag + "']/text()";
                    }

                    if (XPathTerm != null)
                    {
                        try
                        {

                            var rawResult = dicom_doc.XPathEvaluate(XPathTerm);
                            value = EvaluateResult(rawResult);

                        }
                        catch (Exception e)
                        {
                            value = "<![CDATA[" + e.Message + "]]>";
                        }
                    }
                    else
                    {
                        value = filterProperty.Value;
                    }

                    if (existing_item != null && existing_item.Value.Length > value.Length)
                    {
                        value = existing_item.Value;
                    }

                    properties.Properties.Add(filterProperty.CreateInstancePropertyItem(value));

                }
                 
            }

            List<VreciPropertyItem> ItemsToAdd = new List<VreciPropertyItem>();
            foreach (var item in existingProperties.Properties)
            {
                if (properties.FindByReference(item.Reference) == null)
                {
                    ItemsToAdd.Add(item);
                }
            }

            properties.Properties.AddRange(ItemsToAdd);

            return properties;

        }

    }

}
